﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Messaging;
using Sinacor.OMM.Repository;
using Sinacor.OMM.MessageEntity;
//using MSMQ;
using System.Diagnostics;
using Sinacor.OMM.Logging;

namespace Sinacor.OMM.Providers
{
    public class MSMQProvider: IRepositoryProvider, IDisposable
    {

        #region Variaveis e constantes
        
        MessageQueue _mq;
        //MSMQManagement _mqM;
        const string formatname = "FormatName:";
        #endregion

        #region Propriedades
        private string _msmqAddress;

        public string MsmqAddress
        {
            set { 
                _msmqAddress = value;
                SetMSMQ();
            }
        }
        #endregion

        /// <summary>
        /// Construtor
        /// </summary>
        /// <param name="msmqAddress">endereço da fila msmq</param>
        public MSMQProvider(string msmqAddress)
        {
            _mq = new MessageQueue();
            _mq.Formatter = new BinaryMessageFormatter();
            _msmqAddress = msmqAddress;

            SetMSMQ();
        }

        #region IRepositoryProvider Members
        /// <summary>
        /// Envia mensagem para a fila
        /// </summary>
        /// <param name="message"></param>
        public void Put(BaseMessage message)
        {
            try
            {
                _mq.Send(message.Original, string.Concat(message.SequentialID.ToString(), "|", message.ID));
            }
            catch (MessageQueueException exMQ)
            {
                EventLog.WriteEntry(LogSources.MSMQProvider, string.Concat(exMQ.Message, " ", exMQ.StackTrace), EventLogEntryType.Error);
                throw exMQ;
            }
        }

        /// <summary>
        /// Recupera e tira a primeira mensagem da fila
        /// </summary>
        /// <returns></returns>
        public BaseMessage Get()
        {
            BaseMessage message = null;
            try
            {
                Message msg = _mq.Receive(new TimeSpan(0, 0, 5));

                if (msg != null)
                {
                    return TranslateMessageLabel(msg);
                }
                return message;
            }
            catch (MessageQueueException exMQ)
            {
                EventLog.WriteEntry(Sinacor.OMM.Logging.LogSources.MSMQProvider, string.Concat(exMQ.Message, " ", exMQ.StackTrace), EventLogEntryType.Error);
                throw exMQ;
            }
        }

        /// <summary>
        /// Obtem a quantidade de mensagens no respositório
        /// </summary>
        /// <returns></returns>
        public int CountMessage()
        {
            try
            {
                SetMSMQManagement();
                //return _mqM.MessageCount;
                return 0;
            }
            catch (Exception ex)
            {
                EventLog.WriteEntry(Sinacor.OMM.Logging.LogSources.MSMQProvider, string.Concat(ex.Message, " ", ex.StackTrace), EventLogEntryType.Error);
                throw ex;
            }
        }

        #endregion

        #region Métodos publicos 
        /// <summary>
        /// Recupera a primeira mensagem da fila
        /// </summary>
        /// <returns></returns>
        public BaseMessage Peek()
        {
            try
            {
                Message msg = _mq.Peek(new TimeSpan(0, 0, 5));
                if (msg != null)
                {
                    return TranslateMessageLabel(msg);
                }
                else
                    return null;
            }
            catch (InvalidOperationException ex)
            {
                ///se ocorrer um erro ao converter o objeto recuperado da fila
                EventLog.WriteEntry(Sinacor.OMM.Logging.LogSources.MSMQProvider, string.Concat(ex.Message, " ", ex.StackTrace), EventLogEntryType.Error);
                throw ex;
            }
            catch (InvalidCastException ex)
            {
                ///se ocorrer um erro ao converter o objeto recuperado da fila
                EventLog.WriteEntry(Sinacor.OMM.Logging.LogSources.MSMQProvider, string.Concat(ex.Message, " ", ex.StackTrace), EventLogEntryType.Error);
                throw ex;
            }
            catch (MessageQueueException exMQ)
            {
                EventLog.WriteEntry(Sinacor.OMM.Logging.LogSources.MSMQProvider, string.Concat(exMQ.Message, " ", exMQ.StackTrace), EventLogEntryType.Error);
                throw exMQ;
            }
        }

        /// <summary>
        /// Retorna a quantidade de bytes no repositório
        /// </summary>
        /// <returns></returns>
        public long CountBytes()
        {
            try
            {
                SetMSMQManagement();
                //return (long)_mqM.BytesInQueue;
                return 0;
            }
            catch (Exception ex)
            {
                EventLog.WriteEntry(Sinacor.OMM.Logging.LogSources.MSMQProvider, string.Concat(ex.Message, " ", ex.StackTrace), EventLogEntryType.Error);
                throw ex;
            }
        }

        #endregion

        #region Metodos privados

        /// <summary>
        /// 
        /// </summary>
        /// <param name="_message"></param>
        protected BaseMessage TranslateMessageLabel(Message msg)
        {
            BaseMessage baseMessage = new BaseMessage();
            char separator = '|';
            string[] ids;
            baseMessage.Original = (string) msg.Body;
            if (!string.IsNullOrEmpty(msg.Label))
            {
                ids = msg.Label.Split(separator);
                baseMessage.SequentialID = Convert.ToInt32(ids[0]);
                baseMessage.ID = ids[1];
            }

            return baseMessage;
        }

        /// <summary>
        /// Changes MSMQ file 
        /// </summary>
        private void SetMSMQ()
        {
            if (_mq == null)
                _mq = new MessageQueue();

            _mq.Path = string.Concat(formatname, _msmqAddress);
        }

        /// <summary>
        /// Init MSMQ Management
        /// </summary>
        private void SetMSMQManagement()
        {
            //TODO: Verificar se utiliza o substring para ter o nome ou ip da máquina
            object machine = _msmqAddress.Substring(_msmqAddress.LastIndexOf(":") + 1, _msmqAddress.IndexOf(@"\") - _msmqAddress.LastIndexOf(":") - 1);
            object queue = string.Empty;
            object formatName = _msmqAddress;
            //_mqM = null;
            //_mqM = new MSMQManagement();
            //_mqM.Init(ref machine, ref queue, ref formatName);
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            if (_mq != null)
                _mq.Dispose();

            //if (_mqM != null)
            //    _mqM = null;
        }

        #endregion

    }
}
